﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Compilex.Syntatic.Declarations;

namespace Compilex.Syntatic
{
    public class Assignment
    {
        public SyntaticContext Context { get; set; }
        public Symbol LHS { get; set; }
        public int ArrayOffset { get; set; }
        
        public SymbolType LHSType
        {
            get
            {
                return ((VariableDescriptor)LHS.Descriptor).Type;
            }
        }

        public Assignment(SyntaticContext context)
        {
            this.Context = context;
        }

        public void StartOrdinaryVariableLHS()
        {
            string equals = Context.CurrentToken;
            if (equals == "=")
            {
                string lhsId = Context.SyntaticStack.Pop();
                this.LHS = SymbolTableHelper.GetVariableDeclaredAnywhere(lhsId, Context);
            }
            else
            {
                throw new InternalSyntaticException("Assignment.Start chamado e '=' não estava sendo lido.");
            }
            Context.ExpressionStack.StartNewExpression();
        }

        public void StartArrayLHS()
        {
            // começa logo após ler o igual
            Context.SyntaticStack.Pop(); // ]
            this.ArrayOffset = Int32.Parse(Context.SyntaticStack.Pop());
            Context.SyntaticStack.Pop(); // [
            string id = Context.SyntaticStack.Pop();
            this.LHS = SymbolTableHelper.GetArrayDeclaredAnywhere(id, Context);
            if (ArrayOffset >= ((VariableDescriptor)this.LHS.Descriptor).Size)
            {
                throw new SyntaticException("Acesso a posição inexistente do vetor " + LHS.Identifier);
            }
            Context.ExpressionStack.StartNewExpression();
        }

        public void EndBExpression()
        {
            Context.ExpressionStack.EndExpression();
            if (Context.ExpressionStack.JustPopped.Type != this.LHSType)
            {
                throw new SyntaticException(
                    String.Format(@"Tipos incompatíveis: expressão do lado esquerdo é do tipo '{0}', mas expressão do lado direito é do tipo '{1}'", this.LHSType,
                Context.ExpressionStack.JustPopped.Type));
            }
            Finish();
        }

        public void RHSCharacter()
        {
            Context.ExpressionStack.CancelExpression();
            Finish();
        }

        public void RHSLiteral()
        {
            Context.ExpressionStack.CancelExpression();
        }

        public void Finish()
        {
            switch (this.LHS.Class)
            {
                case SymbolClass.Variavex:
                    Context.CommandBlockStack.Current.CodeGenerator.AssignValue(this.LHS);
                    break;
                case SymbolClass.Vetex:
                    Context.CommandBlockStack.Current.CodeGenerator.AssignValueArrayElement(
                        this.LHS, this.ArrayOffset);
                    break;
                case SymbolClass.Funcex:
                case SymbolClass.Estruturex:
                default:
                    throw new SyntaticException("O lado esquerdo de uma atribuição deve ser sempre uma variável ou um vetor.");
            }
            
        }
    }
}
